/*-------------------------------------------------------------------------
 *
 * catcache.h
 *	  低级目录缓存定义。
 *
 * 注意：每个目录缓存必须有一个对应的唯一索引在其缓存的系统表上 --- 即，索引必须匹配用于在该缓存中查找的键。
 * 所有缓存抓取都通过索引扫描完成（在正常情况下）。索引应为唯一，以确保对于一个键组合只能有一行匹配。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/catcache.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef CATCACHE_H
#define CATCACHE_H

#include "access/htup.h"
#include "access/skey.h"
#include "lib/ilist.h"
#include "utils/relcache.h"

/*
 *		struct catctup:			缓存中的单个元组。
 *		struct catclist:		匹配部分键的元组列表。
 *		struct catcache:		管理缓存的信息。
 *		struct catcacheheader:	管理所有缓存的信息。
 */

#define CATCACHE_MAXKEYS		4


/* 计算数据项哈希的函数 */
typedef uint32 (*CCHashFN) (Datum datum);

/* 计算两个数据项相等的函数 */
typedef bool (*CCFastEqualFN) (Datum a, Datum b);

typedef struct catcache
{
	int			id;				/* 缓存标识符 --- 参见 syscache.h */
	int			cc_nbuckets;	/* 此缓存中的哈希桶数量 */
	TupleDesc	cc_tupdesc;		/* 元组描述符（复制自 reldesc） */
	dlist_head *cc_bucket;		/* 哈希桶 */
	CCHashFN	cc_hashfunc[CATCACHE_MAXKEYS];	/* 每个键的哈希函数 */
	CCFastEqualFN cc_fastequal[CATCACHE_MAXKEYS];	/* 每个键的快速相等函数 */
	int			cc_keyno[CATCACHE_MAXKEYS]; /* 每个键的 AttrNumber */
	dlist_head	cc_lists;		/* CatCList 结构的列表 */
	int			cc_ntup;		/* 当前在此缓存中的元组数量 */
	int			cc_nkeys;		/* 键的数量 (1..CATCACHE_MAXKEYS) */
	const char *cc_relname;		/* 元组来源的关系名称 */
	Oid			cc_reloid;		/* 元组来源的关系 OID */
	Oid			cc_indexoid;	/* 匹配缓存键的索引 OID */
	bool		cc_relisshared; /* 该关系是否跨数据库共享? */
	slist_node	cc_next;		/* 列表链接 */
	ScanKeyData cc_skey[CATCACHE_MAXKEYS];	/* 堆扫描的预计算键信息 */

	/*
	 * 将这些放在最后，以便在用 CATCACHE_STATS 编译 catcache.c 时
	 * 不会破坏其他模块的 ABI
	 */
#ifdef CATCACHE_STATS
	long		cc_searches;	/* 针对此缓存的总搜索次数 */
	long		cc_hits;		/* 与现有条目的匹配数量 */
	long		cc_neg_hits;	/* 与负条目的匹配数量 */
	long		cc_newloads;	/* 新条目的成功加载数量 */

	/*
	 * cc_searches - (cc_hits + cc_neg_hits + cc_newloads) 是失败的
	 * 搜索次数，每个都将导致加载一个负条目
	 */
	long		cc_invals;		/* 从缓存中失效的条目数量 */
	long		cc_lsearches;	/* 总的列表搜索次数 */
	long		cc_lhits;		/* 与现有列表的匹配数量 */
#endif
} CatCache;


typedef struct catctup
{
	int			ct_magic;		/* 用于识别 CatCTup 条目 */
#define CT_MAGIC   0x57261502

	uint32		hash_value;		/* 此元组键的哈希值 */

	/*
	 * 该条目的查找键。通过引用的数据项指向正缓存条目的元组，并为负条目单独分配。
	 */
	Datum		keys[CATCACHE_MAXKEYS];

	/*
	 * 缓存中的每个元组都是存储其哈希桶元素的 dlist 的成员。我们保持每个 dlist
	 * 以 LRU 顺序以加快重复查找的速度。
	 */
	dlist_node	cache_elem;		/* 每个桶列表的列表成员 */

	/*
	 * 标记为“无效”的元组不得通过后续搜索返回。
	 * 不过，在其引用计数变为零之前，它不会从缓存中实际删除。 （如果它是 CatCList 的一部分，则该列表的引用计数也必须变为零；此外，请记得在标记元组的同时将列表标记为无效。）
	 *
	 * 负缓存条目是对某个特定键没有匹配元组的断言。就避免目录搜索而言，这与正常条目同样有用。积极和消极条目的管理是相同的。
	 */
	int			refcount;		/* 活跃引用的数量 */
	bool		dead;			/* 死亡但尚未被移除？ */
	bool		negative;		/* 负缓存条目？ */
	HeapTupleData tuple;		/* 元组管理头文件 */

	/*
	 * 元组也可能最多是一个 CatCList 的成员。 （如果一个单独的
	 * catcache 使用不同数量的键进行列表搜索，我们可能必须
	 * 因为这个限制而为同一个元组创建多个条目。
	 * 目前，不期望这种情况很常见，因此我们接受潜在的
	 * 低效。）
	 */
	struct catclist *c_list;	/* 包含 CatCList，若没有则为 NULL */

	CatCache   *my_cache;		/* 链接到拥有的 catcache */
	/* 正确对齐的元组数据，除非是负条目 */
} CatCTup;


/*
 * CatCList 描述了部分搜索的结果，即，仅使用
 * N 键缓存的前 K 键列进行的搜索。我们将所使用的键存储
 * 到 keys 属性中，以表示存储的键集合。CatCList
 * 对象包含指向所有满足部分键的表行的缓存条目的链接。
 * （注意：这些都将不是负缓存条目。）
 *
 * CatCList 仅是每个缓存列表的成员；我们目前不
 * 将它们划分为哈希桶。
 *
 * 被标记为“死亡”的列表不得在后续搜索中返回。
 * 然而，在其引用计数变为零之前，它不会被物理删除
 * 出缓存。 （如果它的任何成员条目是死亡的，则列表应标记为死亡。）
 *
 * 如果“ordered”为 true，则成员元组按缓存底层索引的顺序出现。
 * 在正常操作中这将为真，但在启动或恢复操作期间可能不为真。
 * （namespace.c 在为真时能够节省一些周期。）
 */
typedef struct catclist
{
	int			cl_magic;		/* 用于识别 CatCList 条目 */
#define CL_MAGIC   0x52765103

	uint32		hash_value;		/* 查找键的哈希值 */

	dlist_node	cache_elem;		/* 每个 catcache 列表的列表成员 */

	/*
	 * 条目的查找键，前 nkeys 个元素是有效的。
	 * 所有按引用的部分都是单独分配的。
	 */
	Datum		keys[CATCACHE_MAXKEYS];

	int			refcount;		/* 活跃引用的数量 */
	bool		dead;			/* 死亡但尚未被移除？ */
	bool		ordered;		/* 成员按索引顺序列出？ */
	short		nkeys;			/* 指定的查找键数量 */
	int			n_members;		/* 成员元组的数量 */
	CatCache   *my_cache;		/* 链接到拥有的 catcache */
	CatCTup    *members[FLEXIBLE_ARRAY_MEMBER]; /* members */
} CatCList;


typedef struct catcacheheader
{
	slist_head	ch_caches;		/* CatCache 结构的列表头 */
	int			ch_ntup;		/* 所有缓存中的元组数量 */
} CatCacheHeader;


/* 这个 extern 复制了 utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;

extern void CreateCacheMemoryContext(void);

extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
							  int nkeys, const int *key,
							  int nbuckets);
extern void InitCatCachePhase2(CatCache *cache, bool touch_index);

extern HeapTuple SearchCatCache(CatCache *cache,
								Datum v1, Datum v2, Datum v3, Datum v4);
extern HeapTuple SearchCatCache1(CatCache *cache,
								 Datum v1);
extern HeapTuple SearchCatCache2(CatCache *cache,
								 Datum v1, Datum v2);
extern HeapTuple SearchCatCache3(CatCache *cache,
								 Datum v1, Datum v2, Datum v3);
extern HeapTuple SearchCatCache4(CatCache *cache,
								 Datum v1, Datum v2, Datum v3, Datum v4);
extern void ReleaseCatCache(HeapTuple tuple);

extern uint32 GetCatCacheHashValue(CatCache *cache,
								   Datum v1, Datum v2,
								   Datum v3, Datum v4);

extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys,
									Datum v1, Datum v2,
									Datum v3);
extern void ReleaseCatCacheList(CatCList *list);

extern void ResetCatalogCaches(void);
extern void ResetCatalogCachesExt(bool debug_discard);
extern void CatalogCacheFlushCatalog(Oid catId);
extern void CatCacheInvalidate(CatCache *cache, uint32 hashValue);
extern void PrepareToInvalidateCacheTuple(Relation relation,
										  HeapTuple tuple,
										  HeapTuple newtuple,
										  void (*function) (int, uint32, Oid));

extern void PrintCatCacheLeakWarning(HeapTuple tuple);
extern void PrintCatCacheListLeakWarning(CatCList *list);

#endif							/* CATCACHE_H */
